home *** CD-ROM | disk | FTP | other *** search
- /*
- *
- * u n i x . c -- Some Unix primitives
- *
- * Copyright ⌐ 1993-1996 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
- *
- *
- * Permission to use, copy, and/or distribute this software and its
- * documentation for any purpose and without fee is hereby granted, provided
- * that both the above copyright notice and this permission notice appear in
- * all copies and derived works. Fees for distribution or use of this
- * software or derived works may only be charged with express written
- * permission of the copyright holder.
- * This software is provided ``as is'' without express or implied warranty.
- *
- * This software is a derivative work of other copyrighted softwares; the
- * copyright notices of these softwares are placed in the file COPYRIGHTS
- *
- *
- * Author: Erick Gallesio [eg@kaolin.unice.fr]
- * Creation date: 29-Mar-1994 10:57
- * Last file update: 21-Jul-1996 17:22
- */
- #ifndef WIN32
- # include <unistd.h>
- # include <pwd.h>
- #else
- # include <io.h>
- # define F_OK 00
- # define X_OK 01
- # define W_OK 02
- # define R_OK 04
- #endif
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <dirent.h>
-
- #ifdef WIN32
- /* One of above files includes <stdarg.h> with BC++ (and stdarg and
- * vararg are not compatible
- */
- # undef __STDARG_H
- #endif
-
- #include "stk.h"
-
-
- #ifdef SUNOS4
- /* I avoid to use the POSIX getcwd since it is implemented using popen(3) and
- * pwd(1) on SunOS 4.1.3 ==> It is VERY SLOW.
- */
-
- #define getcwd my_getcwd
- static char *my_getcwd(char *path, int size)
- {
- if (!path) path = (char *) must_malloc(size);
- getwd(path);
- return path;
- }
- #endif /* SUNOS4 */
-
- /******************************************************************************
- *
- * Utilities
- *
- ******************************************************************************/
-
- /*
- * TILDE-EXPAND -- expand '~' and '~user' string prefix
- *
- */
-
- static char *tilde_expand(char *name, char *result)
- {
- char *dir, *p;
-
- #ifdef WIN32
- strcpy(result, name);
- return name;
- #else
- if (name[0] != '~') {
- strcpy(result, name);
- return name;
- }
-
- if ((name[1] == '/') || (name[1] == '\0')) {
- dir = getenv("HOME");
- if (dir == NULL)
- Err("couldn't find HOME environment for expanding ", STk_makestring(name));
-
- sprintf(result, "%s%s", dir, name+1);
- }
- else {
- struct passwd *pwPtr;
- register int len;
- for (p=&name[1]; (*p != 0) && (*p != '/'); p++) {
- /* Null body; just find end of name. */
- }
- len = p-(name+1);
- strncpy(result, name+1, len);
- result[len] = '\0';
-
- pwPtr = getpwnam(result);
- if (pwPtr == NULL) {
- endpwent();
- Err("User does not exist", STk_makestring(result));
- }
- sprintf(result, "%s%s", pwPtr->pw_dir, p);
- endpwent();
- }
- return result;
- #endif
- }
-
- /*
- * ABSOLUTE -- Given a file name, return its (mostly clean) absolute path name
- *
- */
- static void absolute(char *s, char *pathname)
- {
- char *p = pathname;
- char *t;
-
- if (!ISABSOLUTE(s)) {
- getcwd(pathname, MAX_PATH_LENGTH);
- p = &pathname[strlen(pathname)]; /* place p at end of pathname */
- }
- *p = DIRSEP;
-
- for ( ; *s; s++) {
- t = s;
- switch (*s) {
- case '.' : if (*(s+1)) {
- switch (*++s) {
- case '.' : if (ISDIRSEP(*p) && (*(s+1)=='\0' ||
- ISDIRSEP(*(s+1)))) {
- /* We must go back to the parent */
- if (ISDIRSEP(*p) && p > pathname) p --;
- while (p > pathname && !ISDIRSEP(*p)) p--;
- }
- else {
- /* There is a suit of dot. Copy it */
- for (s = t; *s == '.'; s++) *++p = '.';
- s -= 1;
- }
- break;
- #ifdef WIN32
- case '\\':
- #endif
- case '/' : if (!ISDIRSEP(*p)) {
- *++p = '.';
- *++p = DIRSEP;
- }
- break;
- default : *++p = '.'; *++p = *s; break;
- }
- }
- else { /* We have a final (single) dot */
- if (!ISDIRSEP(*p)) *++p = '.';
- }
- break;
- #ifdef WIN32
- case '\\':
- #endif
- case '/' : if (!ISDIRSEP(*p)) *++p = DIRSEP; break;
- default : *++p = *s;
- }
- }
-
- /* Place a \0 at end. If path ends with a "/", delete it */
- if (p == pathname || !ISDIRSEP(*p)) p++;
- *p = '\0';
- }
-
-
- #define MAXLINK 50 /* Number max of link before declaring we have a loop */
-
- SCM STk_resolve_link(char *path, int count)
- {
- #ifdef WIN32
- return STk_internal_expand_file_name(path);
- #else
- char link[MAX_PATH_LENGTH], dst[MAX_PATH_LENGTH], *s, *d=dst;
- int n;
- SCM p;
-
- p = STk_internal_expand_file_name(path);
-
- for (s=CHARS(p)+1, *d++='/' ; ; s++, d++) {
- switch (*s) {
- case '\0':
- case '/' : *d = '\0';
- if ((n=readlink(dst, link, MAX_PATH_LENGTH-1)) > 0) {
- link[n] = '\0';
- if (link[0] == '/')
- /* link is absolute */
- d = dst;
- else {
- /* relative link. Delete last item */
- while (*--d != '/') {
- }
- d += 1;
- }
-
- /* d points the place where the link must be placed */
- if (d - dst + strlen(link) + strlen(s) < MAX_PATH_LENGTH - 1) {
- /* we have enough room */
- sprintf(d, "%s%s", link, s);
- /* Recurse. Be careful for loops (a->b and b->a) */
- if (count < MAXLINK)
- return STk_resolve_link(dst, count+1);
- }
- return Ntruth;
- }
- else {
- if (errno != EINVAL)
- /* EINVAL = file is not a symlink (i.e. it's a true error) */
- return Ntruth;
- else
- if (*s) *d = '/';
- else return STk_makestring(dst);
- }
- default: *d = *s;
- }
- }
- #endif
- }
-
-
- /*
- *----------------------------------------------------------------------
- *
- * fileglob --
- * ***** ******
- * ***** This function is an adaptation of the Tcl function DoGlob ******
- * ***** Adaptated to use true lists rather than string as in Tcl ******
- * ***** ******
- *
- *
- * This recursive procedure forms the heart of the globbing
- * code. It performs a depth-first traversal of the tree
- * given by the path name to be globbed.
- *
- * Results:
- * The return value is a standard Tcl result indicating whether
- * an error occurred in globbing. After a normal return the
- * result in interp will be set to hold all of the file names
- * given by the dir and rem arguments. After an error the
- * result in interp will hold an error message.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- static SCM fileglob(char *dir, char *rem, SCM result)
- /* dir: Name of a directory at which to start glob expansion. This name
- * is fixed: it doesn't contain any globbing chars.
- * rem: Path to glob-expand.
- */
- {
- /*
- * When this procedure is entered, the name to be globbed may
- * already have been partly expanded by ancestor invocations of
- * fileglob. The part that's already been expanded is in "dir"
- * (this may initially be empty), and the part still to expand
- * is in "rem". This procedure expands "rem" one level, making
- * recursive calls to itself if there's still more stuff left
- * in the remainder.
- */
-
- Tcl_DString newName; /* Holds new name consisting of
- * dir plus the first part of rem. */
- register char *p;
- register char c;
- char *openBrace, *closeBrace, *name, *dirName;
- int gotSpecial, baseLength;
- struct stat statBuf;
-
- /*
- * Make sure that the directory part of the name really is a
- * directory. If the directory name is "", use the name "."
- * instead, because some UNIX systems don't treat "" like "."
- * automatically. Keep the "" for use in generating file names,
- * otherwise "glob foo.c" would return "./foo.c".
- */
-
- dirName = (*dir == '\0') ? ".": dir;
- if ((stat(dirName, &statBuf) != 0) || !S_ISDIR(statBuf.st_mode))
- return result;
-
- Tcl_DStringInit(&newName);
-
- /*
- * First, find the end of the next element in rem, checking
- * along the way for special globbing characters.
- */
-
- gotSpecial = 0;
- openBrace = closeBrace = NULL;
- for (p = rem; ; p++) {
- c = *p;
- if ((c == '\0') || ((openBrace == NULL) && (c == '/'))) break;
- if ((c == '{') && (openBrace == NULL)) openBrace = p;
- if ((c == '}') && (openBrace != NULL) && (closeBrace == NULL)) closeBrace = p;
- if ((c == '*') || (c == '[') || (c == '\\') || (c == '?')) gotSpecial = 1;
- }
-
- /*
- * If there is an open brace in the argument, then make a recursive
- * call for each element between the braces. In this case, the
- * recursive call to fileglob uses the same "dir" that we got.
- * If there are several brace-pairs in a single name, we just handle
- * one here, and the others will be handled in recursive calls.
- */
-
- if (openBrace != NULL) {
- char *element;
-
- if (closeBrace == NULL) {
- Tcl_DStringFree(&newName);
- Err("unmatched open-brace in file name", NIL);
- }
-
- Tcl_DStringAppend(&newName, rem, openBrace-rem);
- baseLength = newName.length;
- for (p = openBrace; *p != '}'; ) {
- element = p+1;
- for (p = element; ((*p != '}') && (*p != ',')); p++) {}
- Tcl_DStringAppend(&newName, element, p-element);
- Tcl_DStringAppend(&newName, closeBrace+1, -1);
- result = fileglob(dir, newName.string, result);
- newName.length = baseLength;
- }
- goto done;
- }
-
- /*
- * Start building up the next-level name with dir plus a slash if
- * needed to separate it from the next file name.
- */
-
- Tcl_DStringAppend(&newName, dir, -1);
- if ((dir[0] != 0) && (newName.string[newName.length-1] != '/')) {
- Tcl_DStringAppend(&newName, SDIRSEP, 1);
- }
- baseLength = newName.length;
-
- /*
- * If there were any pattern-matching characters, then scan through
- * the directory to find all the matching names.
- */
-
- if (gotSpecial) {
- DIR *d;
- struct dirent *entryPtr;
- char savedChar;
-
- d = opendir(dirName);
- if (d == NULL) {
- Tcl_DStringFree(&newName);
- Err("cannot read directory", STk_makestring(dirName));
- }
-
- /*
- * Temporarily store a null into rem so that the pattern string
- * is now null-terminated.
- */
-
- savedChar = *p;
- *p = 0;
-
- while (1) {
- entryPtr = readdir(d);
- if (entryPtr == NULL) break;
- /*
- * Don't match names starting with "." unless the "." is
- * present in the pattern.
- */
- if ((*entryPtr->d_name == '.') && (*rem != '.')) continue;
-
- if (Tcl_StringMatch(entryPtr->d_name, rem)) {
- newName.length = baseLength;
- Tcl_DStringAppend(&newName, entryPtr->d_name, -1);
- if (savedChar == 0)
- result = Cons(STk_makestring(newName.string), result);
- else {
- result = fileglob(newName.string, p+1, result);
- if (result != TCL_OK) break;
- }
- }
-
- }
- closedir(d);
- *p = savedChar;
- goto done;
- }
-
- /*
- * The current element is a simple one with no fancy features. Add
- * it to the new name. If there are more elements still to come,
- * then recurse to process them.
- */
-
- Tcl_DStringAppend(&newName, rem, p-rem);
- if (*p != 0) {
- result = fileglob(newName.string, p+1, result);
- goto done;
- }
-
- /*
- * There are no more elements in the pattern. Check to be sure the
- * file actually exists, then add its name to the list being formed
- * in main_interp-result.
- */
-
- name = newName.string;
- if (*name == 0) name = ".";
- if (access(name, F_OK) != 0) goto done;
- result = Cons(STk_makestring(name), result);
- done:
- Tcl_DStringFree(&newName);
- return result;
- }
-
- SCM STk_internal_expand_file_name(char *s)
- {
- char expanded[2 * MAX_PATH_LENGTH], abs[2 * MAX_PATH_LENGTH];
- /* Warning: absolute makes no control about path overflow. Hence the "2 *" */
-
- absolute(tilde_expand(s, expanded), abs);
- return STk_makestring(abs);
- }
-
-
- void STk_whence(char *exec, char *path)
- {
- char *p, *q, dir[MAX_PATH_LENGTH];
- struct stat buf;
-
- if (ISABSOLUTE(exec)) {
- strncpy(path, exec, MAX_PATH_LENGTH);
- return;
- }
-
- #ifdef FREEBSD
- /* I don't understand why this is needed */
- if (access(path, X_OK) == 0) {
- stat(path, &buf);
- if (!S_ISDIR(buf.st_mode)) return;
- }
- #endif
-
- p = getenv("PATH");
- while (*p) {
- /* Copy the stuck of path in dir */
- for (q = dir; *p && *p != PATHSEP; p++, q++) *q = *p;
- *q = '\000';
-
- if (!*dir) {
- /* patch suggested by Erik Ostrom <eostrom@vesuvius.ccs.neu.edu> */
- getcwd(path, MAX_PATH_LENGTH);
- sprintf(path + strlen(path), "%c%s", DIRSEP, exec);
- }
- else
- sprintf(path, "%s%c%s", dir, DIRSEP, exec);
-
- sprintf(path, "%s%c%s", dir, DIRSEP, exec);
- if (access(path, X_OK) == 0) {
- stat(path, &buf);
- if (!S_ISDIR(buf.st_mode)) return;
- }
-
- /* Try next path */
- if (*p) p++;
- }
- /* Not found. Set path to "" */
- path[0] = '\0';
- }
-
- int STk_dirp(const char *path)
- {
- struct stat buf;
-
- if (stat(path, &buf) >= 0)
- return S_ISDIR(buf.st_mode);
- return FALSE;
- }
-
-
- /******************************************************************************
- *
- * Primitives
- *
- ******************************************************************************/
-
- PRIMITIVE STk_expand_file_name(SCM s)
- {
- if (NSTRINGP(s)) Err("expand-file-name: bad string", s);
- return STk_internal_expand_file_name(CHARS(s));
- }
-
- PRIMITIVE STk_canonical_path(SCM str)
- {
- if (NSTRINGP(str)) Err("canonical-path: not a string", str);
- return STk_resolve_link(CHARS(str), 0);
- }
-
- PRIMITIVE STk_getcwd(void)
- {
-
- char *buf = (char *)getcwd(NULL, MAX_PATH_LENGTH);
- SCM z;
-
- if (!buf) Err("getcwd: cannot allocate space", NIL);
- z = STk_makestring(buf);
- free(buf);
-
- return z;
- }
-
- PRIMITIVE STk_chdir(SCM s)
- {
- if (NSTRINGP(s)) Err("chdir: bad string", s);
-
- if (chdir(CHARS(STk_internal_expand_file_name(CHARS(s)))))
- Err("chdir: cannot change directory to", s);
- return UNDEFINED;
- }
-
- PRIMITIVE STk_getpid(void)
- {
- return (STk_makeinteger((int) getpid()));
- }
-
- PRIMITIVE STk_system(SCM com)
- {
- if (NSTRINGP(com)) Err("system: not a string", com);
- return STk_makeinteger(system(CHARS(com)));
- }
-
- PRIMITIVE STk_getenv(SCM str)
- {
- char *tmp;
- if (NSTRINGP(str)) Err("getenv: not a string", str);
- tmp = getenv(CHARS(str));
- return tmp ? STk_makestring(tmp) : Ntruth;
- }
-
- PRIMITIVE STk_setenv(SCM var, SCM value)
- {
- char *s;
- if (NSTRINGP(var)) Err("setenv!: variable is not a string", var);
- if (strchr(CHARS(var), '=')) Err("setenv!: variable contains a '='", var);
- if (NSTRINGP(value)) Err("setenv!: value is not a string", value);
-
- s = malloc(strlen(CHARS(var))+ strlen(CHARS(value)) + 2); /* 2 cause '=' & \0 */
- sprintf(s, "%s=%s", CHARS(var), CHARS(value));
- putenv(s);
- return UNDEFINED;
- }
-
-
- /******************************************************************************
- *
- * file-is-xxx? primitives
- *
- ******************************************************************************/
-
- static SCM my_access(SCM path, int mode, char *who)
- {
- if (NSTRINGP(path)) {
- char buff[100];
- sprintf(buff, "%s: bad string", who);
- Err(buff, path);
- }
-
- return (access(CHARS(path), mode) == 0) ? Truth: Ntruth;
- }
-
-
- static SCM my_stat(SCM path, int mode, char *who)
- {
- struct stat info;
-
- if (NSTRINGP(path)) {
- char buff[100];
- sprintf(buff, "%s: bad string", who);
- Err(buff, path);
- }
-
- if (stat(CHARS(path), &info) != 0) return Ntruth;
-
- switch (mode) {
- case 1: return (S_ISDIR(info.st_mode)) ? Truth : Ntruth;
- case 2: return (S_ISREG(info.st_mode)) ? Truth : Ntruth;
- }
- }
-
- PRIMITIVE STk_file_is_directoryp(SCM f)
- {
- return my_stat(f, 1, "file-is-directory?");
- }
-
- PRIMITIVE STk_file_is_regularp(SCM f)
- {
- return my_stat(f, 2, "file-is-regular?");
- }
-
- PRIMITIVE STk_file_is_readablep(SCM f)
- {
- return my_access(f, R_OK, "file-is-readable?");
- }
-
- PRIMITIVE STk_file_is_writablep(SCM f)
- {
- return my_access(f, W_OK, "file-is-writable?");
- }
-
- PRIMITIVE STk_file_is_executablep(SCM f)
- {
- return my_access(f, X_OK, "file-is-executable?");
- }
-
- PRIMITIVE STk_file_existp(SCM f)
- {
- return my_access(f, F_OK, "file-exists?");
- }
-
-
- PRIMITIVE STk_file_glob(SCM l, int len) /* len is unused here */
- {
- SCM res = NIL;
- char s[2*MAX_PATH_LENGTH];
-
- for ( ; NNULLP(l); l = CDR(l)) {
- if (NSTRINGP(CAR(l))) Err("glob: bad string", CAR(l));
-
- tilde_expand(CHARS(CAR(l)), s);
-
- res = STk_append(LIST2(res,
- (ISDIRSEP(*s)) ? fileglob(SDIRSEP, s+1, NIL) :
- fileglob("", s, NIL)),
- 2);
- }
- return res;
- }
-
-